package com.example.testsbjenkins.zip;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.input.NullInputStream;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author: lt
 * @date: 2022/7/22 17:18
 * @Description: todo
 */
public class ZipFIlesCostTest {

    /**
     * version1 直接文件流的方式
     */
    public static void zipFileNoBuffer() throws IOException {
        String zipFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        String inputFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\fileTest";
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        File inputFile = new File(inputFilePath);
        long total = FileUtils.sizeOfDirectory(inputFile) / (1024 * 1024);
        int length = inputFile.listFiles().length;
        ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(zipFilePath)));
        for (File file : inputFile.listFiles()) {
            InputStream inputStream = new FileInputStream(file);
            zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
            int temp = 0;
            while ((temp = inputStream.read()) != -1) {
                zipOutputStream.write(temp);
            }
        }
        stopWatch.stop();
        System.out.println("zipFileBuffer 压缩" + total + "M,共计" + inputFile.listFiles().length + "个文件成功,耗时:" + stopWatch.getLastTaskTimeMillis() / 1000 + "s");
    }


    /**
     * version2 Buffered的方式
     */
    public static void zipFileBuffer() throws IOException {

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String zipFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        String inputFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\fileTest";
        File zipFile = new File(zipFilePath);
        File inputFile = new File(inputFilePath);

        long total = FileUtils.sizeOfDirectory(inputFile) / (1024 * 1024);

        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(zipOut)) {
            // 开始时间
            for (File file : inputFile.listFiles()) {
                try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
                    zipOut.putNextEntry(new ZipEntry(file.getName()));
                    int temp = 0;
                    while ((temp = bufferedInputStream.read()) != -1) {
                        bufferedOutputStream.write(temp);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        stopWatch.stop();
        System.out.println("zipFileBuffer 压缩" + total + "M,共计" + inputFile.listFiles().length + "个文件成功,耗时:" + stopWatch.getLastTaskTimeMillis() / 1000 + "s");
    }

    /**
     * version3 Channel的方式
     */
    public static void zipFileChannel() throws IOException {
        String zipFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        String inputFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\fileTest";
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        File inputFile = new File(inputFilePath);
        long total = FileUtils.sizeOfDirectory(inputFile) / (1024 * 1024);

        int length = inputFile.listFiles().length;
        File zipFile = new File(zipFilePath);
        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); WritableByteChannel writableByteChannel = Channels.newChannel(zipOut)) {
            for (File file : inputFile.listFiles()) {
                try (FileChannel fileChannel = new FileInputStream(file).getChannel()) {
                    zipOut.putNextEntry(new ZipEntry(file.getName()));
                    fileChannel.transferTo(0, file.length(), writableByteChannel);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        stopWatch.stop();
        System.out.println("zipFileChannel 压缩" + total + "M,共计" + length + "个文件成功,耗时:" + stopWatch.getLastTaskTimeMillis() / 1000 + "s");
    }

    //Version 4 使用Map映射文件 
    /**
     * -----zipFileMap() zipFileMap1() 区别在于 对于io关闭的操作
     * 
     * zipFileMap1() 使用的是jdk1.7后的操作 
     * 从java1.7版本开始,支持使用try后面跟随（）括号管理释放资源,前提是这些可关闭的资源必须实现 java.lang.AutoCloseable 接口。
     * @throws IOException
     */
    public static void zipFileMap() throws IOException {
        String from = null;
        try {
            from = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\fileTest";
        } catch (Exception e) {
            e.printStackTrace();
        }
        String to = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        File file = new File(from);
        File zipFile = new File(to);
        long total = FileUtils.sizeOfDirectory(file) / (1024 * 1024);
        int length = file.listFiles().length;
        ZipOutputStream zipOut = null;
        WritableByteChannel writableByteChannel = null;
        try {
             zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
             writableByteChannel = Channels.newChannel(zipOut);
            for (File f : file.listFiles()) {
                zipOut.putNextEntry(new ZipEntry(f.getName()));
                //内存中的映射文件
                MappedByteBuffer mappedByteBuffer = new RandomAccessFile(f, "r")
                        .getChannel()
                        .map(FileChannel.MapMode.READ_ONLY, 0, f.length());
                writableByteChannel.write(mappedByteBuffer);
            }

        }  catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (writableByteChannel != null) writableByteChannel.close();
            if (zipOut != null) zipOut.close();
        }
        stopWatch.stop();
        System.out.println("zipFileMap 压缩" + total + "M,共计" + length + "个文件成功,耗时:" + stopWatch.getLastTaskTimeMillis() / 1000 + "s");
    }



    //Version 4 使用Map映射文件 
    public static void zipFileMap1() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //开始时间
        String from = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\fileTest";
        String to = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        File file = new File(from);
        File zipFile = new File(to);
        int length = file.listFiles().length;
        long total = FileUtils.sizeOfDirectory(file) / (1024 * 1024);
        try (
                ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
                WritableByteChannel writableByteChannel = Channels.newChannel(zipOut)) {
            for (File f: file.listFiles()) {
                zipOut.putNextEntry(new ZipEntry(f.getName()));
                //内存中的映射文件
                MappedByteBuffer mappedByteBuffer = new RandomAccessFile(f, "r")
                        .getChannel()
                        .map(FileChannel.MapMode.READ_ONLY, 0, f.length());
                writableByteChannel.write(mappedByteBuffer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        stopWatch.stop();
        System.out.println("zipFileMap 压缩" + total + "M,共计" + length + "个文件成功,耗时:" + stopWatch.getLastTaskTimeMillis() / 1000 + "s"); 
    }
    
    /**
     * version5 通过并发的方式 压缩文件
     **/
    public static void compressFileList() throws IOException, ExecutionException, InterruptedException {
        String zipOutName = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        String sourceFilePath = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\big";

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        File sourceFile = new File(sourceFilePath);
        long total = FileUtils.sizeOfDirectory(sourceFile) / (1024 * 1024);
        File[] sourceFiles = sourceFile.listFiles();
        ThreadFactory factory = new ThreadFactoryBuilder().setNamePrefix("compressFileList-pool-").build();
        ExecutorService executor = new ThreadPoolExecutor(8, 30, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory);
        ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executor);
        OutputStream outputStream = new FileOutputStream(zipOutName);
        ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
        zipArchiveOutputStream.setEncoding("UTF-8");
        for (File inFile : sourceFiles) {
            final InputStreamSupplier inputStreamSupplier = () -> {
                try {
                    return new FileInputStream(inFile);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    return new NullInputStream(0);
                }
            };
            ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(inFile.getName());
            zipArchiveEntry.setMethod(ZipArchiveEntry.DEFLATED);
            zipArchiveEntry.setSize(inFile.length());
            zipArchiveEntry.setUnixMode(UnixStat.FILE_FLAG | 436);
            parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
        }
        parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
        zipArchiveOutputStream.close();
        outputStream.close();
        stopWatch.stop();
        System.out.println("compressFileList 压缩" + total + "M,共计" + sourceFiles.length + "个文件成功,耗时:" + stopWatch.getLastTaskTimeMillis() / 1000 + "s");

    }


    /**
     * zero copy
     */
    private static void channelCopy() throws IOException {
        long startTime = System.currentTimeMillis();
        String from = "D:\\谷歌Download\\ideaIU-2021.1.3.win.zip";
        String to = "C:\\Users\\Administrator.DESKTOP-4PCK80Q\\Desktop\\test\\zip\\1.zip";
        
        RandomAccessFile inputFile = new RandomAccessFile(from, "r");
        RandomAccessFile outputFile = new RandomAccessFile(to, "rw");

        FileChannel inputChannel = inputFile.getChannel();
        FileChannel outputChannel = outputFile.getChannel();

        inputChannel.transferTo(0, inputFile.length(), outputChannel);

        inputChannel.close();
        outputChannel.close();

        long endTime = System.currentTimeMillis();
        System.out.println("channelCopy cost:" + (endTime - startTime)/1000+"s");
    }




    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        // zipFileNoBuffer();
        // zipFileBuffer();
        //zipFileChannel();
        //zipFileMap();
        //zipFileConcurrent();
        compressFileList();
        //channelCopy();
        //zipFileMap();
        //zipFileMap1();
    }
}
